/**
 * Copyright (C) 2008 by The Regents of the University of California
 * Redistribution of this file is permitted under the terms of the GNU
 * Public License (GPL).
 *
 * @author Junghoo "John" Cho <cho AT cs.ucla.edu>
 * @date 3/24/2008
 */

#include <cstdio>
#include <iostream>
#include <fstream>
#include "Bruinbase.h"
#include "SqlEngine.h"
#include "BTreeIndex.h"

using namespace std;

// external functions and variables for load file and sql command parsing 
extern FILE* sqlin;
int sqlparse(void);

RC SqlEngine::run(FILE* commandline)
{
  fprintf(stdout, "Bruinbase> ");

  // set the command line input and start parsing user input
  sqlin = commandline;
  sqlparse();  // sqlparse() is defined in SqlParser.tab.c generated from
               // SqlParser.y by bison (bison is GNU equivalent of yacc)

  return 0;
}

RC SqlEngine::select(int attr, const string& table, const vector<SelCond>& cond)
{
  RecordFile rf;   // RecordFile containing the table
  RecordId   rid;  // record cursor for table scanning

  RC     rc;
  int    key;     
  string value;
  int    count;
  int    diff;

  // open the table file
  if ((rc = rf.open(table + ".tbl", 'r')) < 0) {
    fprintf(stderr, "Error: table %s does not exist\n", table.c_str());
    return rc;
  }
  
  BTreeIndex index;
  if(index.open(table + ".idx", 'r') == 0)	//if index exists
  {
	count = 0;
	int k;
	int lowerBound = -1;
	int upperBound = -1;
	bool isFound = false;
	bool haveLowerBound = false;
	bool haveUpperBound = false;
	int compValue;
	for(k = 0; k < cond.size(); k++)
	{
		if(cond[k].attr == 1 && cond[k].comp != SelCond::NE)
		{
			isFound = true;
			compValue = atoi(cond[k].value);
			switch (cond[k].comp)
			{
				case SelCond::GT:
				case SelCond::GE:
				haveLowerBound = true;
				if(compValue > lowerBound)
					lowerBound = compValue;
				break;
				case SelCond::LT:
				case SelCond::LE:
				haveUpperBound = true;
				if(upperBound == -1)
				{
					upperBound = compValue;
				}
				else if(compValue < upperBound)
				{
					upperBound = compValue;
				}
				break;
				case SelCond::EQ:
				haveLowerBound = haveUpperBound = true;
				upperBound = lowerBound = compValue;
				break;
			}
		}
	}
				
			
	if(!isFound)	goto no_index;
	IndexCursor cursor;
	if(!haveLowerBound)
	{
		index.locate(-1, cursor);
		while((index.readForward(cursor, key, rid) == 0) && (key <= upperBound))
		{
			rf.read(rid, key, value);
			for (unsigned i = 0; i < cond.size(); i++) {
			  // compute the difference between the tuple value and the condition value
			  switch (cond[i].attr) {
			  case 1:
			diff = key - atoi(cond[i].value);
			break;
			  case 2:
			diff = strcmp(value.c_str(), cond[i].value);
			break;
			  }

			  // skip the tuple if any condition is not met
			  switch (cond[i].comp) {
			  case SelCond::EQ:
			if (diff != 0) goto index_next_tuple_lt;
			break;
			  case SelCond::NE:
			if (diff == 0) goto index_next_tuple_lt;
			break;
			  case SelCond::GT:
			if (diff <= 0) goto index_next_tuple_lt;
			break;
			  case SelCond::LT:
			if (diff >= 0) goto index_next_tuple_lt;
			break;
			  case SelCond::GE:
			if (diff < 0) goto index_next_tuple_lt;
			break;
			  case SelCond::LE:
			if (diff > 0) goto index_next_tuple_lt;
			break;
			  }
			}

			// the condition is met for the tuple. 
			// increase matching tuple counter
			count++;

			// print the tuple 
			switch (attr) {
			case 1:  // SELECT key
			  fprintf(stdout, "%d\n", key);
			  break;
			case 2:  // SELECT value
			  fprintf(stdout, "%s\n", value.c_str());
			  break;
			case 3:  // SELECT *
			  fprintf(stdout, "%d '%s'\n", key, value.c_str());
			  break;
			}
			index_next_tuple_lt:
			int rubbish;
		}
	}
	else if(!haveUpperBound)
	{
		index.locate(lowerBound, cursor);
		while(index.readForward(cursor, key, rid) == 0)
		{
			rf.read(rid, key, value);
			for (unsigned i = 0; i < cond.size(); i++) {
			  // compute the difference between the tuple value and the condition value
			  switch (cond[i].attr) {
			  case 1:
			diff = key - atoi(cond[i].value);
			break;
			  case 2:
			diff = strcmp(value.c_str(), cond[i].value);
			break;
			  }

			  // skip the tuple if any condition is not met
			  switch (cond[i].comp) {
			  case SelCond::EQ:
			if (diff != 0) goto index_next_tuple_gt;
			break;
			  case SelCond::NE:
			if (diff == 0) goto index_next_tuple_gt;
			break;
			  case SelCond::GT:
			if (diff <= 0) goto index_next_tuple_gt;
			break;
			  case SelCond::LT:
			if (diff >= 0) goto index_next_tuple_gt;
			break;
			  case SelCond::GE:
			if (diff < 0) goto index_next_tuple_gt;
			break;
			  case SelCond::LE:
			if (diff > 0) goto index_next_tuple_gt;
			break;
			  }
			}

			// the condition is met for the tuple. 
			// increase matching tuple counter
			count++;

			// print the tuple 
			switch (attr) {
			case 1:  // SELECT key
			  fprintf(stdout, "%d\n", key);
			  break;
			case 2:  // SELECT value
			  fprintf(stdout, "%s\n", value.c_str());
			  break;
			case 3:  // SELECT *
			  fprintf(stdout, "%d '%s'\n", key, value.c_str());
			  break;
			}
			index_next_tuple_gt:
			int rubbish;
		}
	}
	else
	{
		index.locate(lowerBound, cursor);
		while((index.readForward(cursor, key, rid) == 0) && (key <= upperBound))
		{
			rf.read(rid, key, value);
			for (unsigned i = 0; i < cond.size(); i++) {
			  // compute the difference between the tuple value and the condition value
			  switch (cond[i].attr) {
				case 1:
					diff = key - atoi(cond[i].value);
				break;
				case 2:
					diff = strcmp(value.c_str(), cond[i].value);
				break;
			  }

			  // skip tuple if either condition not met
			  switch (cond[i].comp) {
				case SelCond::EQ:
					if (diff != 0) goto index_next_tuple_eq;
				break;
				case SelCond::NE:
					if (diff == 0) goto index_next_tuple_eq;
				break;
				case SelCond::GT:
					if (diff <= 0) goto index_next_tuple_eq;
				break;
				case SelCond::LT:
					if (diff >= 0) goto index_next_tuple_eq;
				break;
				case SelCond::GE:
					if (diff < 0) goto index_next_tuple_eq;
				break;
				case SelCond::LE:
					if (diff > 0) goto index_next_tuple_eq;
				break;
			  }
			}

			// the condition is met for the tuple. 
			// increase matching tuple counter
			count++;

			// print the tuple 
			switch (attr) {
			case 1:  // SELECT key
			  fprintf(stdout, "%d\n", key);
			  break;
			case 2:  // SELECT value
			  fprintf(stdout, "%s\n", value.c_str());
			  break;
			case 3:  // SELECT *
			  fprintf(stdout, "%d '%s'\n", key, value.c_str());
			  break;
			}
			index_next_tuple_eq:
			int rubbish;
		}
	}
	if (attr == 4) {
		fprintf(stdout, "%d\n", count);
	}
	rc = 0;
	index.close();
  }
  
  else
  {
	no_index:
	  // scan the table file from the beginning
	  rid.pid = rid.sid = 0;
	  count = 0;
	  while (rid < rf.endRid()) {
		// read the tuple
		if ((rc = rf.read(rid, key, value)) < 0) {
		  fprintf(stderr, "Error: while reading a tuple from table %s\n", table.c_str());
		  goto exit_select;
		}

		// check the conditions on the tuple
		for (unsigned i = 0; i < cond.size(); i++) {
		  // compute the difference between the tuple value and the condition value
		  switch (cond[i].attr) {
		  case 1:
		diff = key - atoi(cond[i].value);
		break;
		  case 2:
		diff = strcmp(value.c_str(), cond[i].value);
		break;
		  }

		  // skip the tuple if any condition is not met
		  switch (cond[i].comp) {
		  case SelCond::EQ:
		if (diff != 0) goto next_tuple;		break;
		  case SelCond::NE:
		if (diff == 0) goto next_tuple;		break;
		  case SelCond::GT:
		if (diff <= 0) goto next_tuple;		break;
		  case SelCond::LT:
		if (diff >= 0) goto next_tuple;		break;
		  case SelCond::GE:
		if (diff < 0) goto next_tuple;		break;
		  case SelCond::LE:
		if (diff > 0) goto next_tuple;		break;
		  }
		}

		// the condition is met for the tuple. 
		// increase matching tuple counter
		count++;

		// print the tuple 
		switch (attr) {
		case 1:  // SELECT key
		  fprintf(stdout, "%d\n", key);		  				break;
		case 2:  // SELECT value
		  fprintf(stdout, "%s\n", value.c_str());		  	break;
		case 3:  // SELECT *
		  fprintf(stdout, "%d '%s'\n", key, value.c_str()); break;
		}

		// move to the next tuple
		next_tuple:
		++rid;
	  }
	  // print matching tuple count if "select count(*)"
	  if (attr == 4) {
		fprintf(stdout, "%d\n", count);
	  }
	  rc = 0;
  }

  // close the table file and return
  exit_select:
  rf.close();
  return rc;
}

RC SqlEngine::load(const string& table, const string& loadfile, bool index)
{
	RecordFile rf;
	string filename = table + ".tbl";
	rf.open(filename, 'w');
	ifstream infile;
	infile.open(loadfile.c_str());
	string line;
	if(index == false)	{
		while(getline(infile, line)){
			int key;
			string value;
			if(parseLoadLine(line, key, value) == 0){
				RecordId rec;
				rf.append(key, value, rec);
			}
		}
	}	
	else{
		string pfname = table + ".idx";
		BTreeIndex index;
		index.open(pfname, 'w');
		while(getline(infile, line)){
			int key;
			string value;
			if(parseLoadLine(line, key, value) == 0){
				RecordId rec;
				rf.append(key, value, rec);
				index.insert(key, rec);
			}
		}
		index.close();
	}
	infile.close();
	rf.close();
}

RC SqlEngine::parseLoadLine(const string& line, int& key, string& value)
{
    const char *s;
    char        c;
    string::size_type loc;
    
    // ignore beginning white spaces
    c = *(s = line.c_str());
    while (c == ' ' || c == '\t') { c = *++s; }

    // get the integer key value
    key = atoi(s);

    // look for comma
    s = strchr(s, ',');
    if (s == NULL) { return RC_INVALID_FILE_FORMAT; }

    // ignore white spaces
    do { c = *++s; } while (c == ' ' || c == '\t');
    
    // if there is nothing left, set the value to empty string
    if (c == 0) { 
        value.erase();
        return 0;
    }

    // is the value field delimited by ' or "?
    if (c == '\'' || c == '"') {
        s++;
    } else {
        c = '\n';
    }

    // get the value string
    value.assign(s);
    loc = value.find(c, 0);
    if (loc != string::npos) { value.erase(loc); }

    return 0;
}

/**
 * Test Cases Output
Bruinbase> Bruinbase> 8
Bruinbase> 272 'Baby Take a Bow'
1578 'G.I. Blues'
2244 'King Creole'
2342 'Last Ride, The'
Bruinbase> Bruinbase> Bruinbase> 50
Bruinbase> 173 'Angel Levine, The'
175 'Angel Unchained'
272 'Baby Take a Bow'
303 'Bananas'
395 'Big Jake'
489 'Blue Hawaii'
Bruinbase> Bruinbase> Bruinbase> 100
Bruinbase> 489 'Blue Hawaii'
Bruinbase> Bruinbase> Bruinbase> 1000
Bruinbase> 4506 'Waterworld'
4515 'Wedding Party, The'
4524 'Welcome to the Dollhouse'
4531 'Wharf Rat, The'
4546 'When Night Is Falling'
4558 'While You Were Sleeping'
4560 'White Mans Burden'
4565 'White Wolves II: Legend of the Wild'
4570 'Who Is Harry Kellerman and Why Is He Saying Those Terrible Things About Me?'
4579 'Widows Kiss'
4581 'Wigstock: The Movie'
4583 'Wild Angels, The'
4584 'Wild Bill'
4589 'Wild Ride, The'
4601 'Windrunner'
4619 'Witch Hunt'
4620 'Witchboard III: The Possession'
4621 'Witchcraft 7: Judgement Hour'
4633 'Wizards of the Demon Sword'
4637 'Wolves, The'
4657 'Wrecking Crew, The'
4660 'Wrong Woman, The'
4673 'Yao a yao yao dao waipo qiao'
4683 'Young Poisoners Handbook, The'
4700 'Zooman'
4710 'By Way of the Stars'
4727 'Sabrina, the Teenage Witch'
4732 '¡Dispara!'
4733 'la folie'
Bruinbase> 4506 'Waterworld'
4515 'Wedding Party, The'
4524 'Welcome to the Dollhouse'
4531 'Wharf Rat, The'
4546 'When Night Is Falling'
4558 'While You Were Sleeping'
4560 'White Mans Burden'
4565 'White Wolves II: Legend of the Wild'
4570 'Who Is Harry Kellerman and Why Is He Saying Those Terrible Things About Me?'
4579 'Widows Kiss'
4581 'Wigstock: The Movie'
4583 'Wild Angels, The'
4584 'Wild Bill'
4589 'Wild Ride, The'
4601 'Windrunner'
4619 'Witch Hunt'
4620 'Witchboard III: The Possession'
4621 'Witchcraft 7: Judgement Hour'
4633 'Wizards of the Demon Sword'
4637 'Wolves, The'
4657 'Wrecking Crew, The'
4660 'Wrong Woman, The'
4673 'Yao a yao yao dao waipo qiao'
4683 'Young Poisoners Handbook, The'
4700 'Zooman'
4710 'By Way of the Stars'
4727 'Sabrina, the Teenage Witch'
4732 '¡Dispara!'
4733 'la folie'
Bruinbase> Bruinbase> Bruinbase> 12278
Bruinbase> 4240 'Tommy Boy'
Bruinbase> 402 'Big Squeeze, The'
403 'Big Tease, The'
405 'Bigfoot: The Unforgettable Encounter'
407 'Biker Zombies'
408 'Bikini Bistro'
409 'Bikini Drive-In'
410 'Bikini Hoe-Down'
412 'Bikini Traffic School'
413 'Billy Elliot'
415 'Billys Holiday'
416 'Billys Hollywood Screen Kiss'
418 'Bio-Dome'
420 'Bird of Prey'
421 'Birdcage, The'
422 'Birthday Girl'
423 'BitterSweet'
424 'Black and White'
425 'Black Cat Run'
427 'Black Day Blue Night'
428 'Black Dog'
430 'Black Hawk Down'
431 'Black Knight'
433 'Black Out'
435 'Black Rose of Harlem'
436 'Black Scorpion'
437 'Black Scorpion II: Aftershock'
439 'Black Sea 213'
440 'Black Sheep'
442 'Black Widow Escort'
443 'Blackjack'
444 'BlackMale'
445 'Blackout, The'
447 'Blacktop'
448 'Blackwater Trail'
450 'Blade'
452 'Blair Witch Project, The'
453 'Blast'
454 'Blast from the Past'
457 'Bless the Child'
458 'Blessed Art Thou'
459 'Blind Faith'
460 'Blind Heat'
462 'Bliss'
463 'Blonde Heaven'
464 'Blondes Have More Guns'
465 'Blood & Donuts'
467 'Blood and Wine'
468 'Blood Money'
471 'Blood of the Innocent'
472 'Blood Oranges, The'
474 'Blood, Guts, Bullets and Octane'
477 'Bloodhounds'
479 'Bloodmoon'
480 'Bloodsport 2'
481 'Bloody Murder'
484 'Blow'
485 'Blow Dry'
486 'Blowback'
489 'Blue Hawaii'
490 'Blue Juice'
491 'Blue Moon'
492 'Blue Ridge Fall'
493 'Blues Brothers 2000'
496 'Bobby G. Cant Swim'
Bruinbase> Bruinbase> 
**/
